package com.boot.controller;

import cn.hutool.json.JSONException;
import cn.hutool.json.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.boot.entity.Fragment;
import com.boot.entity.Result;
import com.boot.interceptor.LimitFlowInterceptor;
import com.boot.service.IFragmentService;
import com.boot.util.FileUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.net.URLEncoder;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static com.boot.config.WebConfig.FILE_DIR;
import static com.boot.util.FileUtil.*;

/**
 * @author bbyh
 * @since 2024-04-27
 */
@RestController
@RequestMapping("/fragment")
@Slf4j
@Api("/分片文件接口")
public class FragmentController {
    @Resource
    private IFragmentService fragmentService;
    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @ApiOperation("分片文件上传接口")
    @PostMapping("/upload")
    public Result upload(@RequestBody MultipartFile file, @RequestParam String fragmentMd5, @RequestParam String fileMd5,
                         @RequestParam Integer fragmentSize, @RequestParam String uuid, HttpServletRequest request) {
        try {
            LimitFlowInterceptor.updateFlow(stringRedisTemplate, request, uuid, (int) file.getSize());

            String originalFilename = file.getOriginalFilename();
            String path = FILE_DIR + fragmentMd5 + File.separator;

            assert originalFilename != null;
            int lastIndexOf = originalFilename.lastIndexOf(FRAGMENT_SPLIT);
            Fragment fragment = new Fragment();
            fragment.setFileMd5(md5ToBytes(fileMd5));
            fragment.setFragmentMd5(md5ToBytes(fragmentMd5));
            fragment.setFragmentName(originalFilename);
            fragment.setFragmentOrder(Integer.parseInt(originalFilename.substring(lastIndexOf + FRAGMENT_SPLIT.length())));
            fragment.setFragmentPath(path + originalFilename);
            fragment.setFragmentSize(fragmentSize);
            fragment.setCreateTime(LocalDateTime.now());
            fragmentService.save(fragment);

            FileUtil.upload(file.getInputStream(), originalFilename, path);

            return Result.success("分片文件：" + originalFilename + "上传成功", null);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return Result.error(null, "分片文件上传接口服务出错");
        }
    }

    @ApiOperation("分片文件md5校验接口")
    @GetMapping("/checkMd5")
    public Result checkMd5(@RequestParam String fragmentMd5) {
        try {
            LambdaQueryWrapper<Fragment> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(Fragment::getFragmentMd5, md5ToBytes(fragmentMd5));
            Fragment fragment = fragmentService.getOne(wrapper);
            if (fragment != null) {
                return Result.success("分片文件MD5已存在", null);
            } else {
                return Result.error("分片文件MD5不存在", null);
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return Result.error(null, "分片文件md5校验接口服务出错");
        }
    }

    @ApiOperation("分片文件下载接口")
    @GetMapping("/download")
    public void download(@RequestParam String fragmentMd5, @RequestParam String uuid, HttpServletRequest request, HttpServletResponse response) {
        try {
            LambdaQueryWrapper<Fragment> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(Fragment::getFragmentMd5, md5ToBytes(fragmentMd5));
            Fragment fragment = fragmentService.getOne(wrapper);

            if (fragment == null) {
                log.error("该md5：" + fragmentMd5 + ", 对应的分片文件不存在");
                Result.writeResult(response, "该md5：" + fragmentMd5 + ", 对应的分片文件不存在");
            } else {
                File file = new File(fragment.getFragmentPath());
                if (!file.exists()) {
                    log.error("该md5：" + fragmentMd5 + ", 对应的分片文件在磁盘中不存在");
                    Result.writeResult(response, "该md5：" + fragmentMd5 + ", 对应的分片文件在磁盘中不存在");
                    return;
                }
                LimitFlowInterceptor.updateFlow(stringRedisTemplate, request, uuid, fragment.getFragmentSize());

                byte[] buf = new byte[EACH_FRAGMENT_SIZE];
                try (FileInputStream inputStream = new FileInputStream(file)) {
                    int read = inputStream.read(buf);
                    response.setContentType("application/octet-stream");
                    response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fragment.getFragmentName(), "UTF-8"));
                    ServletOutputStream outputStream = response.getOutputStream();
                    outputStream.write(buf, 0, read);
                    outputStream.flush();
                }
            }
        } catch (Exception e) {
            log.error("服务器文件下载出错", e);
            Result.writeResult(response, "分片文件下载接口服务出错");
        }
    }

    @ApiOperation("获取分片文件列表接口")
    @GetMapping("/getFragmentList")
    public Result getFragmentList(@RequestParam String fileMd5) {
        try {
            LambdaQueryWrapper<Fragment> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(Fragment::getFileMd5, md5ToBytes(fileMd5));
            List<Fragment> fragmentList = fragmentService.list(wrapper);

            List<Map<String, Object>> list = new ArrayList<>(fragmentList.size());
            for (Fragment fragment : fragmentList) {
                Map<String, Object> objectMap = new HashMap<>();
                objectMap.put("fragmentId", fragment.getFragmentId());
                objectMap.put("fileMd5", fileMd5);
                objectMap.put("fragmentMd5", bytesToMd5(fragment.getFragmentMd5()));
                objectMap.put("fragmentName", fragment.getFragmentName());
                objectMap.put("fragmentOrder", fragment.getFragmentOrder());
                objectMap.put("fragmentPath", fragment.getFragmentPath());
                objectMap.put("fragmentSize", fragment.getFragmentSize());
                objectMap.put("createTime", fragment.getCreateTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
                list.add(objectMap);
            }

            JSONObject jsonObject = new JSONObject();
            jsonObject.putOnce("fragmentInfoList", list);
            return Result.success(jsonObject, "分片文件列表获取成功");
        } catch (JSONException e) {
            log.error(e.getMessage(), e);
            return Result.error(null, "获取分片文件列表接口服务出错");
        }
    }

}
